/* 
* Copyright (c) 2010, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Bio.Data.Interfaces;
using Bio.Views.Structure.Editor.Internal.Editing;
using Bio.Views.Structure.Editor.Internal.Rendering.Interfaces;
using Bio.Views.Structure.Editor.Internal.Rendering.Primitives;

namespace Bio.Views.Structure.Editor.Internal.Rendering.Visuals
{
    public class BasePairStructureElementVisualComponent : DependencyObject, IStructureElementVisualComponent
    {
        #region Events

        //Fire this event if you want any capable parent components to deliver data-driven defaults....its not necessary to fire this event to get defaults provided from the parent tree.
        public event EventHandler StartingBasePairPropertyResolve;

        private void OnStartingBasePairPropertyResolve()
        {
            if (StartingBasePairPropertyResolve != null)
                StartingBasePairPropertyResolve(this, EventArgs.Empty);
        }

        //Must fire this event if you fired a StartingBasePairPropertyResolve event, of the parent tree could get corrupted from a default property providing point of view.
        public event EventHandler EndingBasePairPropertyResolve;

        private void OnEndingBasePairPropertyResolve()
        {
            if (EndingBasePairPropertyResolve != null)
                EndingBasePairPropertyResolve(this, EventArgs.Empty);
        }

        public event EventHandler NotifyBasePairVisualEdited;

        private void OnBasePairVisualEdited()
        {
            if (NotifyBasePairVisualEdited != null && !PauseEditNotification)
                NotifyBasePairVisualEdited(this, EventArgs.Empty);
        }

        #endregion

        #region Dependency Properties

        public static readonly DependencyProperty NucleotideFontFamilyProperty = DependencyProperty.Register("NucleotideFontFamily",
            typeof(FontFamily), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideFontWeightProperty = DependencyProperty.Register("NucleotideFontWeight",
            typeof(FontWeight?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideFontStyleProperty = DependencyProperty.Register("NucleotideFontStyle",
            typeof(FontStyle?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideFontSizeProperty = DependencyProperty.Register("NucleotideFontSize",
            typeof(double?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideRenderingStyleProperty = DependencyProperty.Register("NucleotideRenderingStyle",
            typeof(NucleotideRenderingMode?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideFillProperty = DependencyProperty.Register("NucleotideFill",
            typeof(Brush), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty IsNucleotideSubstitutedProperty = DependencyProperty.Register("IsNucleotideSubstituted",
           typeof(bool?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideSubstitutionProperty = DependencyProperty.Register("NucleotideSubstitution",
            typeof(char?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleDiameterProperty = DependencyProperty.Register("NucleotideCircleDiameter",
            typeof(double?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleEdgeThicknessProperty = DependencyProperty.Register("NucleotideCircleEdgeThickness",
            typeof(double?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleIsFilledProperty = DependencyProperty.Register("NucleotideCircleIsFilled",
            typeof(bool?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleFillProperty = DependencyProperty.Register("NucleotideCircleFill",
            typeof(Brush), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty NucleotideCircleStrokeProperty = DependencyProperty.Register("NucleotideCircleStroke",
            typeof(Brush), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnNucleotideVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorRenderingStyleProperty = DependencyProperty.Register("BasePairConnectorRenderingStyle",
            typeof(BasePairConnectorRenderingMode?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnBasePairVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorStrokeProperty = DependencyProperty.Register("BasePairConnectorStroke",
            typeof(Brush), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnBasePairVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorStrokeThicknessProperty = DependencyProperty.Register("BasePairConnectorStrokeThickness",
            typeof(double?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnBasePairVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorSizeProperty = DependencyProperty.Register("BasePairConnectorSize",
            typeof(double?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnBasePairVisualPropertyChanged));

        /*public static readonly DependencyProperty BasePairConnectorOffsetProperty = DependencyProperty.Register("BasePairConnectorOffset",
            typeof(double?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata());*/

        public static readonly DependencyProperty BasePairConnectorFillProperty = DependencyProperty.Register("BasePairConnectorFill",
            typeof(Brush), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnBasePairVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorIsVisibleProperty = DependencyProperty.Register("BasePairConnectorIsVisible",
            typeof(bool?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnBasePairVisualPropertyChanged));

        public static readonly DependencyProperty BasePairConnectorIsFilledProperty = DependencyProperty.Register("BasePairConnectorIsFilled",
            typeof(bool?), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null, OnBasePairVisualPropertyChanged));

        private static readonly DependencyPropertyKey DataPropertyKey = DependencyProperty.RegisterReadOnly("Data",
            typeof(IBasePairBioEntity), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata());
        public static readonly DependencyProperty DataProperty = DataPropertyKey.DependencyProperty;

        public IBasePairBioEntity Data
        {
            get { return (IBasePairBioEntity)GetValue(DataProperty); }
            private set { SetValue(DataPropertyKey, value); }
        }

        private static readonly DependencyPropertyKey ContextMessagePropertyKey = DependencyProperty.RegisterReadOnly("ContextMessage",
            typeof(string), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(null));
        public static readonly DependencyProperty ContextMessageProperty = ContextMessagePropertyKey.DependencyProperty;

        public string ContextMessage
        {
            get { return (string)GetValue(ContextMessageProperty); }
            private set { SetValue(ContextMessagePropertyKey, value); }
        }

        private static readonly DependencyPropertyKey LocationPropertyKey = DependencyProperty.RegisterReadOnly("Location",
            typeof(Point), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(OnBasePairVisualPropertyChanged));
        public static readonly DependencyProperty LocationProperty = LocationPropertyKey.DependencyProperty;

        public Point Location
        {
            get { return (Point)GetValue(LocationProperty); }
            private set { SetValue(LocationPropertyKey, value); }
        }

        private static readonly DependencyPropertyKey RayAnglePropertyKey = DependencyProperty.RegisterReadOnly("RayAngle",
            typeof(double), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(OnBasePairVisualPropertyChanged));
        public static readonly DependencyProperty RayAngleProperty = RayAnglePropertyKey.DependencyProperty;

        public double RayAngle
        {
            get { return (double)GetValue(RayAngleProperty); }
            private set { SetValue(RayAnglePropertyKey, value); }
        }

        private static readonly DependencyPropertyKey MagnitudePropertyKey = DependencyProperty.RegisterReadOnly("Magnitude",
            typeof(double), typeof(BasePairStructureElementVisualComponent), new FrameworkPropertyMetadata(OnBasePairVisualPropertyChanged));
        public static readonly DependencyProperty MagnitudeProperty = MagnitudePropertyKey.DependencyProperty;

        public double Magnitude
        {
            get { return (double)GetValue(MagnitudeProperty); }
            private set { SetValue(MagnitudePropertyKey, value); }
        }

        private static void OnNucleotideVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            BasePairStructureElementVisualComponent bpVisual = d as BasePairStructureElementVisualComponent;
            if (bpVisual != null)
            {
                bpVisual.PropagateVisualPropertyChanged();
            }

            if (args.Property == NucleotideRenderingStyleProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideRenderingStyle = args.NewValue as NucleotideRenderingMode?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideFontWeightProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideFontWeight = args.NewValue as FontWeight?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideFontStyleProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideFontStyle = args.NewValue as FontStyle?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideFontFamilyProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideFontFamily = args.NewValue as FontFamily;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideFontSizeProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideFontSize = args.NewValue as double?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideFillProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideFill = args.NewValue as Brush;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideSubstitutionProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideSubstitution = args.NewValue as char?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == IsNucleotideSubstitutedProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.IsNucleotideSubstituted = args.NewValue as bool?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideCircleDiameterProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideCircleDiameter = args.NewValue as double?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideCircleEdgeThicknessProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideCircleEdgeThickness = args.NewValue as double?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideCircleFillProperty && !bpVisual.PauseEditTracking)
            {
                if (bpVisual.PropertyContextManager != null)
                {
                    bpVisual.PropertyContextManager.NucleotideCircleFill = args.NewValue as Brush;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideCircleIsFilledProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideCircleIsFilled = args.NewValue as bool?;
                    bpVisual.OnBasePairVisualEdited();
                }
            }

            if (args.Property == NucleotideCircleStrokeProperty)
            {
                if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                {
                    bpVisual.PropertyContextManager.NucleotideCircleStroke = args.NewValue as Brush;
                    bpVisual.OnBasePairVisualEdited();
                }
            }
        }

        private static void OnBasePairVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            BasePairStructureElementVisualComponent bpVisual = d as BasePairStructureElementVisualComponent;
            if (bpVisual != null)
            {
                if (args.Property == BasePairConnectorRenderingStyleProperty ||
                    args.Property == BasePairConnectorStrokeThicknessProperty ||
                    args.Property == BasePairConnectorSizeProperty ||
                    args.Property == RayAngleProperty ||
                    args.Property == MagnitudeProperty ||
                    args.Property == LocationProperty)
                {
                    bpVisual.Bounds = bpVisual.PhysicalBPVisual.Measure();
                    bpVisual.PhysicalBPVisual.Render();
                }

                if (args.Property == BasePairConnectorFillProperty ||
                    args.Property == BasePairConnectorIsFilledProperty ||
                    args.Property == BasePairConnectorIsVisibleProperty ||
                    args.Property == BasePairConnectorStrokeProperty)
                    bpVisual.PhysicalBPVisual.Render();

                if (args.Property == BasePairConnectorFillProperty)
                {
                    if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                    {
                        bpVisual.PropertyContextManager.BasePairConnectorFill = args.NewValue as Brush;
                        bpVisual.OnBasePairVisualEdited();
                    }
                }

                if (args.Property == BasePairConnectorIsFilledProperty)
                {
                    if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                    {
                        bpVisual.PropertyContextManager.BasePairConnectorIsFilled = args.NewValue as bool?;
                        bpVisual.OnBasePairVisualEdited();
                    }
                }

                if (args.Property == BasePairConnectorIsVisibleProperty)
                {
                    if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                    {
                        bpVisual.PropertyContextManager.BasePairConnectorIsVisible = args.NewValue as bool?;
                        bpVisual.OnBasePairVisualEdited();
                    }
                }

                if (args.Property == BasePairConnectorRenderingStyleProperty)
                {
                    if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                    {
                        bpVisual.PropertyContextManager.BasePairConnectorRenderingStyle = args.NewValue as BasePairConnectorRenderingMode?;
                        bpVisual.OnBasePairVisualEdited();
                    }
                }

                if (args.Property == BasePairConnectorSizeProperty)
                {
                    if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                    {
                        bpVisual.PropertyContextManager.BasePairConnectorSize = args.NewValue as double?;
                        bpVisual.OnBasePairVisualEdited();
                    }
                }

                if (args.Property == BasePairConnectorStrokeProperty)
                {
                    if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                    {
                        bpVisual.PropertyContextManager.BasePairConnectorStroke = args.NewValue as Brush;
                        bpVisual.OnBasePairVisualEdited();
                    }
                }

                if (args.Property == BasePairConnectorStrokeThicknessProperty)
                {
                    if (bpVisual.PropertyContextManager != null && !bpVisual.PauseEditTracking)
                    {
                        bpVisual.PropertyContextManager.BasePairConnectorStrokeThickness = args.NewValue as double?;
                        bpVisual.OnBasePairVisualEdited();
                    }
                }
            }
        }

        #endregion

        #region Properties

        public Rect Bounds
        {
            get;
            private set;
        }

        #endregion

        #region IStructureElementVisualComponent Members

        public IEnumerable<int> NucleotideIndices
        {
            get { return Enumerable.Range(Data.FivePrimeIndex, 1).Union(Enumerable.Range(Data.ThreePrimeIndex, 1)); }
        }

        public Visual VisualComponent 
        {
            get { return PhysicalBPVisual; }
        }

        private IStructureElementVisualComponent _parentComponent;
        public IStructureElementVisualComponent ParentComponent
        {
            get { return _parentComponent; }
            set
            {
                _parentComponent = value;
                NotifyParentHierarchyChanged();
            }
        }

        public IEnumerable<IStructureElementVisualComponent> Children
        {
            get { return ChildrenInternal.AsEnumerable<IStructureElementVisualComponent>(); }
        }

        public bool IsComponentVisible
        {
            get { return _isComponentVisible; }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.Visible = value;
                    OnBasePairVisualEdited();
                }
                _isComponentVisible = value;
                foreach (var child in ChildrenInternal)
                {
                    child.InterruptEditTracking();
                    child.IsComponentVisible = value;
                    child.UninterruptEditTracking();
                }
                PhysicalBPVisual.Render();
            }
        }

        public void AddChild(IStructureElementVisualComponent child)
        {
            if (child != null && !ChildrenInternal.Contains(child))
            {
                ChildrenInternal.Add(child);
                child.ParentComponent = this;
            }
        }

        public void RemoveChild(IStructureElementVisualComponent child)
        {
            if (child != null && ChildrenInternal.Contains(child))
            {
                ChildrenInternal.Remove(child);
                child.ParentComponent = null;
            }
        }

        public bool HitTest(Point point, bool includeChildren)
        {
            if (Bounds.Contains(point))
                return true;

            if (includeChildren)
            {
                foreach (var child in ChildrenInternal)
                {
                    if (child.HitTest(point, includeChildren))
                        return true;
                }
            }

            return false;
        }

        public void NotifyParentHierarchyChanged()
        {
            ContextMessage = BuildContextMessage();
            foreach (var child in ChildrenInternal)
                child.NotifyParentHierarchyChanged();
        }

        public void PropagateVisualPropertyChanged()
        {
            foreach (var child in ChildrenInternal)
                child.PropagateVisualPropertyChanged();
            Bounds = PhysicalBPVisual.Measure();
            PhysicalBPVisual.Render();
        }

        public void MakeSelectable()
        {
            foreach (var child in ChildrenInternal)
                child.MakeSelectable();
        }

        public void MakeUnselectable()
        {
            foreach (var child in ChildrenInternal)
                child.MakeUnselectable();
        }

        public void Select(bool showNucleotideConnectors)
        {
            foreach (var child in ChildrenInternal)
                child.Select(showNucleotideConnectors);
        }

        public void Deselect()
        {
            foreach (var child in ChildrenInternal)
                child.Deselect();
        }

        public void ContextuallySelect()
        {
        }

        public void ContextuallyDeselect()
        {
        }

        public Visual MakeVisualSnapshot()
        {
            BasePairDrawingVisual visual = new BasePairDrawingVisual(this);
            visual.Measure();
            visual.Render();
            return visual;
        }

        public bool? BasePairConnectorIsFilled 
        {
            get
            {
                bool? val = GetValue(BasePairConnectorIsFilledProperty) as bool?;
                if (!val.HasValue)
                {
                    //If were not explicitly set, we look up the food chain and we have to
                    //catch the NotSupportedException in case our parent dosen't support this property.
                    if (ParentComponent != null)
                    {
                        try
                        {
                            OnStartingBasePairPropertyResolve();
                            bool? inheritedBasePairConnectorFilled = ParentComponent.BasePairConnectorIsFilled;
                            OnEndingBasePairPropertyResolve();
                            if (inheritedBasePairConnectorFilled.HasValue)
                                return inheritedBasePairConnectorFilled;
                        }
                        catch (NotSupportedException) { }
                    }
                    return INTERNAL_DEFAULT_CONNECTOR_ISFILLED;
                }
                else { return val; }
            }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorIsFilled = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorIsFilledProperty, value);
                UninterruptEditNotification();
            }
        }
        
        public bool? BasePairConnectorIsVisible 
        {
            get
            {
                bool? val = GetValue(BasePairConnectorIsVisibleProperty) as bool?;
                if (!val.HasValue)
                {
                    //If were not explicitly set, we look up the food chain and we have to
                    //catch the NotSupportedException in case our parent dosen't support this property.
                    if (ParentComponent != null)
                    {
                        try
                        {
                            OnStartingBasePairPropertyResolve();
                            bool? inheritedBasePairConnectorIsVisible = ParentComponent.BasePairConnectorIsVisible;
                            OnEndingBasePairPropertyResolve();
                            if (inheritedBasePairConnectorIsVisible.HasValue)
                                return inheritedBasePairConnectorIsVisible;
                        }
                        catch (NotSupportedException) { }
                    }
                    return INTERNAL_DEFAULT_CONNECTOR_ISVISIBLE;
                }
                else { return val; }
            }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorIsVisible = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorIsVisibleProperty, value);
                UninterruptEditNotification();
            }
        }
        
        public Brush BasePairConnectorFill 
        {
            get
            {
                Brush val = GetValue(BasePairConnectorFillProperty) as Brush;
                if (val != null)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain and we have to
                    //catch the NotSupportedException in case our parent dosen't support this property.
                    if (ParentComponent != null)
                    {
                        try
                        {
                            OnStartingBasePairPropertyResolve();
                            Brush inheritedBasePairConnectorFill = ParentComponent.BasePairConnectorFill;
                            OnEndingBasePairPropertyResolve();
                            if (inheritedBasePairConnectorFill != null)
                                return inheritedBasePairConnectorFill;
                        }
                        catch (NotSupportedException) { }
                    }
                    return INTERNAL_DEFAULT_CONNECTOR_FILL;
                }
            }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorFill = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorFillProperty, value);
                UninterruptEditNotification();
            }
        }
        
        public Brush BasePairConnectorStroke 
        {
            get
            {
                Brush val = GetValue(BasePairConnectorStrokeProperty) as Brush;
                if (val != null)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain and we have to
                    //catch the NotSupportedException in case our parent dosen't support this property.
                    if (ParentComponent != null)
                    {
                        try
                        {
                            OnStartingBasePairPropertyResolve();
                            Brush inheritedBasePairConnectorStroke = ParentComponent.BasePairConnectorStroke;
                            OnEndingBasePairPropertyResolve();
                            if (inheritedBasePairConnectorStroke != null)
                                return inheritedBasePairConnectorStroke;
                        }
                        catch (NotSupportedException) { }
                    }
                    return INTERNAL_DEFAULT_CONNECTOR_STROKE;
                }
            }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorStroke = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorStrokeProperty, value);
                UninterruptEditNotification();
            }
        }
        
        public double? BasePairConnectorSize 
        {
            get
            {
                double? val = GetValue(BasePairConnectorSizeProperty) as double?;
                if (!val.HasValue)
                {
                    //If were not explicitly set, we look up the food chain and we have to
                    //catch the NotSupportedException in case our parent dosen't support this property.
                    if (ParentComponent != null)
                    {
                        try
                        {
                            OnStartingBasePairPropertyResolve();
                            double? inheritedBasePairConnectorSize = ParentComponent.BasePairConnectorSize;
                            OnEndingBasePairPropertyResolve();
                            if (inheritedBasePairConnectorSize.HasValue)
                                return inheritedBasePairConnectorSize;
                        }
                        catch (NotSupportedException) { }
                    }
                    return INTERNAL_DEFAULT_CONNECTOR_SIZE;
                }
                else { return val; }
            }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorSize = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorSizeProperty, value);
                UninterruptEditNotification();
            }
        }
        
        public double? BasePairConnectorStrokeThickness 
        {
            get
            {
                double? val = GetValue(BasePairConnectorStrokeThicknessProperty) as double?;
                if (!val.HasValue)
                {
                    //If were not explicitly set, we look up the food chain and we have to
                    //catch the NotSupportedException in case our parent dosen't support this property.
                    if (ParentComponent != null)
                    {
                        try
                        {
                            OnStartingBasePairPropertyResolve();
                            double? inheritedBasePairConnectorStrokeThickness = ParentComponent.BasePairConnectorStrokeThickness;
                            OnEndingBasePairPropertyResolve();
                            if (inheritedBasePairConnectorStrokeThickness.HasValue)
                                return inheritedBasePairConnectorStrokeThickness;
                        }
                        catch (NotSupportedException) { }
                    }
                    return INTERNAL_DEFAULT_CONNECTOR_STROKETHICKNESS;
                }
                else { return val; }
            }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorStrokeThickness = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorStrokeThicknessProperty, value);
                UninterruptEditNotification();
            }
        }

        public BasePairConnectorRenderingMode? BasePairConnectorRenderingStyle 
        {
            get
            {
                BasePairConnectorRenderingMode? val = GetValue(BasePairConnectorRenderingStyleProperty) as BasePairConnectorRenderingMode?;
                if (!val.HasValue)
                {
                    //If were not explicitly set, we look up the food chain and we have to
                    //catch the NotSupportedException in case our parent dosen't support this property.
                    if (ParentComponent != null)
                    {
                        try
                        {
                            OnStartingBasePairPropertyResolve();
                            BasePairConnectorRenderingMode? inheritedBasePairConnectorRenderingStyle = ParentComponent.BasePairConnectorRenderingStyle;
                            OnEndingBasePairPropertyResolve();
                            if (inheritedBasePairConnectorRenderingStyle.HasValue)
                                return inheritedBasePairConnectorRenderingStyle;
                        }
                        catch (NotSupportedException) { }
                    }
                    return INTERNAL_DEFAULT_CONNECTOR_RENDERINGSTYLE;
                }
                else { return val; }
            }
            set
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.BasePairConnectorRenderingStyle = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(BasePairConnectorRenderingStyleProperty, value);
                UninterruptEditNotification();
            }
        }

        public FontFamily NucleotideFontFamily
        {
            get 
            {
                FontFamily val = GetValue(NucleotideFontFamilyProperty) as FontFamily;
                if (val != null)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideFontFamily;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFontFamily = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFontFamilyProperty, value);
                UninterruptEditNotification();
            }
        }

        public FontWeight? NucleotideFontWeight
        {
            get 
            {
                FontWeight? val = (FontWeight?)GetValue(NucleotideFontWeightProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideFontWeight;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFontWeight = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFontWeightProperty, value);
                UninterruptEditNotification();
            }
        }

        public FontStyle? NucleotideFontStyle
        {
            get 
            {
                FontStyle? val = (FontStyle?)GetValue(NucleotideFontStyleProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideFontStyle;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFontStyle = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFontStyleProperty, value);
                UninterruptEditNotification();
            }
        }

        public double? NucleotideFontSize
        {
            get 
            {
                double? val = (double?)GetValue(NucleotideFontSizeProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideFontSize;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFontSize = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFontSizeProperty, value);
                UninterruptEditNotification();
            }
        }

        public NucleotideRenderingMode? NucleotideRenderingStyle
        {
            get 
            { 
                NucleotideRenderingMode? val = (NucleotideRenderingMode?)GetValue(NucleotideRenderingStyleProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideRenderingStyle;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideRenderingStyle = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideRenderingStyleProperty, value);
                UninterruptEditNotification();
            }
        }

        public Brush NucleotideFill
        {
            get 
            {
                Brush val = GetValue(NucleotideFillProperty) as Brush;
                if (val != null)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideFill;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideFill = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideFillProperty, value);
                UninterruptEditNotification();
            }
        }

        public char? NucleotideSubstitution 
        {
            get
            {
                char? val = (char?)GetValue(NucleotideSubstitutionProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideSubstitution;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideSubstitution = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideSubstitutionProperty, value);
                UninterruptEditNotification();
            }
        }

        public bool? IsNucleotideSubstituted 
        {
            get
            {
                bool? val = (bool?)GetValue(IsNucleotideSubstitutedProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.IsNucleotideSubstituted;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.IsNucleotideSubstituted = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(IsNucleotideSubstitutedProperty, value);
                UninterruptEditNotification();
            }
        }

        public double? NucleotideCircleDiameter 
        {
            get
            {
                double? val = (double?)GetValue(NucleotideCircleDiameterProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideCircleDiameter;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleDiameter = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleDiameterProperty, value);
                UninterruptEditNotification();
            }
        }

        public double? NucleotideCircleEdgeThickness 
        {
            get
            {
                double? val = (double?)GetValue(NucleotideCircleEdgeThicknessProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideCircleEdgeThickness;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleEdgeThickness = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleEdgeThicknessProperty, value);
                UninterruptEditNotification();
            }
        }

        public bool? NucleotideCircleIsFilled 
        {
            get
            {
                bool? val = (bool?)GetValue(NucleotideCircleIsFilledProperty);
                if (val.HasValue)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideCircleIsFilled;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleIsFilled = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleIsFilledProperty, value);
                UninterruptEditNotification();
            }
        }

        public Brush NucleotideCircleFill 
        {
            get
            {
                Brush val = (Brush)GetValue(NucleotideCircleFillProperty);
                if (val != null)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideCircleFill;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleFill = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleFillProperty, value);
                UninterruptEditNotification();
            }
        }

        public Brush NucleotideCircleStroke 
        {
            get
            {
                Brush val = (Brush)GetValue(NucleotideCircleStrokeProperty);
                if (val != null)
                {
                    return val;
                }
                else
                {
                    //If were not explicitly set, we look up the food chain.
                    if (ParentComponent != null)
                        return ParentComponent.NucleotideCircleStroke;
                    else
                        return null;
                }
            }
            set 
            {
                if (PropertyContextManager != null && !PauseEditTracking)
                {
                    PropertyContextManager.NucleotideCircleStroke = value;
                    OnBasePairVisualEdited();
                }
                InterruptEditNotification(); //Hack....Override change to dependency property propagating as an edit.
                SetValue(NucleotideCircleStrokeProperty, value);
                UninterruptEditNotification();
            }
        }

        public bool IsNucleotideFillInheriting()
        {
            var fill = GetValue(NucleotideFillProperty);
            return (fill == null) ? true : false;
        }

        public bool IsNucleotideCircleFillInheriting()
        {
            var fill = GetValue(NucleotideCircleFillProperty);
            return (fill == null) ? true : false;
        }

        public bool IsNucleotideCircleStrokeInheriting()
        {
            var stroke = GetValue(NucleotideCircleStrokeProperty);
            return (stroke == null) ? true : false;
        }

        public bool IsBasePairConnectorFillInheriting()
        {
            var connfill = GetValue(BasePairConnectorFillProperty);
            return (connfill == null) ? true : false;
        }

        public bool IsBasePairConnectorStrokeInheriting()
        {
            var connstroke = GetValue(BasePairConnectorStrokeProperty);
            return (connstroke == null) ? true : false;
        }

        public void StartEditTracking()
        {
            if (PropertyContextManager == null)
            {
                PropertyContextManager = new BasePairVisualComponentPropertyContext(this);
            }
        }

        public void ApplyPropertyEditSet(StructureElementPropertyChangeSet set, bool interruptEditTracking)
        {
            if (PropertyContextManager != null)
            {
                if (PropertyContextManager.BatchEdit(set))
                {
                    PauseEditTracking = true;
                    PropertyContextManager.Apply(); //Synchronize
                    PauseEditTracking = false;
                    if(!interruptEditTracking) OnBasePairVisualEdited();
                }
            }
        }

        public void InterruptEditTracking()
        {
            PauseEditTracking = true;
        }

        public void UninterruptEditTracking()
        {
            PauseEditTracking = false;
        }

        public void InterruptEditNotification()
        {
            PauseEditNotification = true;
        }

        public void UninterruptEditNotification()
        {
            PauseEditNotification = false;
        }

        public void UndoAllPropertyChanges()
        {
            if (PropertyContextManager != null)
            {
                PropertyContextManager.UndoAll();
                InterruptEditTracking();
                PropertyContextManager.Apply(); //We tell the property context manager to apply the rolled back state to us
                UninterruptEditTracking();
            }
        }

        public void UndoLastPropertyChange()
        {
            if (PropertyContextManager != null)
            {
                PropertyContextManager.UndoLast();
                InterruptEditTracking();
                PropertyContextManager.Apply(); //We tell the property context manager to apply the rolled back state to us
                UninterruptEditTracking();
            }
        }

        public object SynchronizeProperties()
        {
            if (PropertyContextManager != null)
            {
                return PropertyContextManager.Save(); //We don't dump the edited state
            }
            return null;
        }

        public object SaveProperties()
        {
            if (PropertyContextManager != null)
            {
                object retValue = PropertyContextManager.Save();
                PropertyContextManager = new BasePairVisualComponentPropertyContext(this);
                return retValue;
            }
            return null;
        }

        public bool IsDirty()
        {
            if (PropertyContextManager == null)
                return false;
            return (PropertyContextManager.EditCount() > 0) ? true : false;
        }

        #endregion

        public void FivePrimeNucleotideMovedTo(Point location)
        {
            BasePairVector newRenderingVector = new BasePairVector(location, RenderingVector.ThreePrimeNucleotideLocation);
            UpdateRenderingVector(newRenderingVector);
        }

        public void FivePrimeNucleotideMovedBy(Point delta)
        {
            Point newFivePrimeNucleotideLocation = new Point(RenderingVector.FivePrimeNucleotideLocation.X + delta.X, RenderingVector.FivePrimeNucleotideLocation.Y + delta.Y);
            BasePairVector newRenderingVector = new BasePairVector(newFivePrimeNucleotideLocation, RenderingVector.ThreePrimeNucleotideLocation);
            UpdateRenderingVector(newRenderingVector);
        }

        public void ThreePrimeNucleotideMovedTo(Point location)
        {
            BasePairVector newRenderingVector = new BasePairVector(RenderingVector.FivePrimeNucleotideLocation, location);
            UpdateRenderingVector(newRenderingVector);
        }

        public void ThreePrimeNucleotideMovedBy(Point delta)
        {
            Point newThreePrimeNucleotideLocation = new Point(RenderingVector.ThreePrimeNucleotideLocation.X + delta.X, RenderingVector.ThreePrimeNucleotideLocation.Y + delta.Y);
            BasePairVector newRenderingVector = new BasePairVector(RenderingVector.FivePrimeNucleotideLocation, newThreePrimeNucleotideLocation);
            UpdateRenderingVector(newRenderingVector);
        }

        /*public Rect PreviewMoveTo(Point location)
        {
            Point delta = new Point(location.X - Location.X, location.Y - Location.Y);
            return PreviewMoveBy(delta);
        }*/

        /*public Rect PreviewMoveBy(Point delta)
        {
            return Rect.Offset(Bounds, delta.X, delta.Y);
        }*/

        /*public void MoveTo(Point location)
        {
            Point delta = new Point(location.X - Location.X, location.Y - Location.Y);
            MoveBy(delta);
        }*/

        public void MoveBy(Point delta)
        {
            Location = new Point(Location.X + delta.X, Location.Y + delta.Y);
            RenderingVector.MoveBy(delta);
            Bounds = PhysicalBPVisual.Measure();
            PhysicalBPVisual.Render();
        }

        public Point ChangeBasePairWidth(double desiredWidth)
        {
            Point retValue = RenderingVector.ChangeBasePairWidth(desiredWidth);
            Location = RenderingVector.Center;
            Magnitude = RenderingVector.Distance;
            RayAngle = RenderingVector.RayAngle;
            return retValue;
        }

        public Point RotateBasePairTo(double desiredAngleInDegrees)
        {
            Point retValue = RenderingVector.RotateBasePairTo(desiredAngleInDegrees);
            Location = RenderingVector.Center;
            Magnitude = RenderingVector.Distance;
            RayAngle = RenderingVector.RayAngle;
            return retValue;
        }

        public Point RotateBasePairBy(double desiredRotationInDegrees)
        {
            Point retValue = RenderingVector.RotateBasePairBy(desiredRotationInDegrees);
            Location = RenderingVector.Center;
            Magnitude = RenderingVector.Distance;
            RayAngle = RenderingVector.RayAngle;
            return retValue;
        }

        static BasePairStructureElementVisualComponent()
        {
            INTERNAL_DEFAULT_CONNECTOR_FILL.Freeze();
            INTERNAL_DEFAULT_CONNECTOR_STROKE.Freeze();
        }

        public BasePairStructureElementVisualComponent(IBasePairBioEntity data) 
            : this(new BasePairVector(), data)
        {
        }

        public BasePairStructureElementVisualComponent(BasePairVector renderingVector, IBasePairBioEntity data)
        {
            Debug.Assert(data != null);
            Debug.Assert(renderingVector != null);
            Data = data;
            UpdateRenderingVector(renderingVector);
            ChildrenInternal = new List<IStructureElementVisualComponent>();
            PhysicalBPVisual = new BasePairDrawingVisual(this);
            ContextMessage = BuildContextMessage();
            PauseEditNotification = false;
            PauseEditTracking = false;
        }

        private bool _isComponentVisible = true;
        private BasePairVisualComponentPropertyContext PropertyContextManager { get; set; }
        private bool PauseEditTracking { get; set; }
        private bool PauseEditNotification { get; set; }
        private BasePairDrawingVisual PhysicalBPVisual { get; set; }
        private BasePairVector RenderingVector { get; set; }
        private List<IStructureElementVisualComponent> ChildrenInternal { get; set; }

        private void UpdateRenderingVector(BasePairVector newRenderingVector)
        {
            Debug.Assert(newRenderingVector != null);
            RenderingVector = newRenderingVector;
            Location = RenderingVector.Center;
            Magnitude = RenderingVector.Distance;
            RayAngle = RenderingVector.RayAngle;
        }

        private string BuildContextMessage()
        {
            HelixStructureElementVisualComponent parentHelix = null;
            if (ParentComponent != null)
            {
                parentHelix = ParentComponent as HelixStructureElementVisualComponent;
                if (parentHelix != null)
                {
                    return string.Format(@"Base Pair {0}-{1}, Member of helix with 5' strand from {0}-{1} and 3' strand from {2}-{3}", Data.FivePrimeIndex + 1, Data.ThreePrimeIndex + 1, parentHelix.FivePrimeStartNucleotideIndex + 1, parentHelix.FivePrimeEndNucleotideIndex + 1, parentHelix.ThreePrimeStartNucleotideIndex + 1, parentHelix.ThreePrimeEndNucleotideIndex + 1);
                }
                else
                {
                    return "Unknown";
                }
            }
            else
            {
                return "Unknown.";
            }
        }

        private static readonly BasePairConnectorRenderingMode INTERNAL_DEFAULT_CONNECTOR_RENDERINGSTYLE = BasePairConnectorRenderingMode.Line;
        private static readonly bool INTERNAL_DEFAULT_CONNECTOR_ISFILLED = false;
        private static readonly bool INTERNAL_DEFAULT_CONNECTOR_ISVISIBLE = true;
        private static readonly Brush INTERNAL_DEFAULT_CONNECTOR_FILL = new SolidColorBrush(Colors.DarkGoldenrod);
        private static readonly double INTERNAL_DEFAULT_CONNECTOR_SIZE = 0.0; //Let the rendered scale based on the base pair
        private static readonly double INTERNAL_DEFAULT_CONNECTOR_STROKETHICKNESS = 0.5;
        private static readonly Brush INTERNAL_DEFAULT_CONNECTOR_STROKE = new SolidColorBrush(Colors.DarkGoldenrod);
    }
}
